package yc.star.friend.service.impl;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import yc.star.common.constants.CacheConstant;
import yc.star.common.constants.Constant;
import yc.star.common.constants.HttpConstants;
import yc.star.common.domain.AppResult;
import yc.star.common.domain.LoginUser;
import yc.star.common.domain.vo.LoginUserVo;
import yc.star.common.enums.ResultCode;
import yc.star.common.enums.UserIdentity;
import yc.star.common.message.service.AliSmsService;
import yc.star.common.redis.service.RedisService;
import yc.star.common.security.exception.ApplicationException;
import yc.star.common.security.service.TokenService;
import yc.star.common.utils.ThreadLocalUtil;
import yc.star.friend.domain.user.User;
import yc.star.friend.domain.user.dto.UserDTO;
import yc.star.friend.domain.user.dto.UserUpdateDTO;
import yc.star.friend.domain.user.vo.UserVO;
import yc.star.friend.manager.UserCacheManager;
import yc.star.friend.mapper.UserMapper;
import yc.star.friend.service.IUserService;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AliSmsService aliSmsService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private UserCacheManager userCacheManager;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;
    @Value("${sms.send-limit:50}")
    private Integer sendLimit;
    @Value("${jwt.secret}")
    private String secret; // jwt安全密钥
    @Value("${sms.is-send:false}")
    private boolean isSend; // 开关 true：打开 false：关闭
    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;


    @Override
    public boolean sendCode(UserDTO userDTO) {
        // 声明手机验证码存储到redis中的key值
        String phoneCodeKey = CacheConstant.PHONE_CODE_KEY+userDTO.getPhone();
        // 声明用户一天点击次数存储到redis中的key值
        String codeTimeKey = CacheConstant.CODE_TIME_KEY+userDTO.getPhone();
        // 获取到验证码剩余有效时间
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if (expire != null && (phoneCodeExpiration*60 - expire) < 60) {
            throw new ApplicationException(AppResult.failed("操作频繁，请一分钟后再试"));
        }
        // 从redis中获取用户点击登录的次数
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        // 判断用户在一天中点击次数是否超过50次
        if (sendTimes != null && sendTimes >= sendLimit) {
            throw new ApplicationException(AppResult.failed("您的当天登录次数已达到上限，请明日再试"));
        }
        // 生成6位的随机验证码
        String code = isSend ? RandomUtil.randomNumbers(6) : "123456";
        // 将验证码存储到redis中,并设置过期时间
        redisService.setCacheObject(phoneCodeKey,code,phoneCodeExpiration, TimeUnit.MINUTES);
        // 将验证码发送到用户的手机上
        boolean send = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
        if (!send) {
            throw new ApplicationException(AppResult.failed("验证码发送失败"));
        }
        // 登录操作次数增加1
        redisService.increment(codeTimeKey);
        // 表明该用户是当天第一次进行登录请求
        if (sendTimes == null) {
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(@NonNull String phone,@NonNull String code) {
        // 声明手机验证码存储到redis中的key值
        String phoneCodeKey = CacheConstant.PHONE_CODE_KEY+phone;
        // 获取到验证码
        String realCode = redisService.getCacheObject(phoneCodeKey, String.class);
        if (StrUtil.isEmpty(realCode)) {
            throw new ApplicationException(AppResult.failed("验证码已过期，请重新发送"));
        }
        if (!realCode.equals(code)) {
            throw new ApplicationException(AppResult.failed("验证码不正确,请重新再试"));
        }

        User loginUser;
        // 通过手机号在数据库中查找用户
        User existUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getPhone, phone));
        if (existUser == null) {
            // 新用户，执行注册功能
            User addUser = new User();
            addUser.setPhone(phone);
            addUser.setStatus(1);
            userMapper.insert(addUser);
            loginUser = addUser;
        } else {
            // 用户为老用户，直接进行登录操作
            loginUser = existUser;
        }

        // 在redis中删除验证码
        redisService.deleteObject(phoneCodeKey);
        // 生成token,并将敏感信息存储到redis中
        String token = tokenService.createToken(loginUser.getUserId(), secret
                , UserIdentity.ORDINARY.getValue(), loginUser.getNickName(),loginUser.getHeadImage());
        return token;
    }

    @Override
    public boolean logout(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public LoginUserVo info(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setNickName(loginUser.getNickName());
        if (StrUtil.isNotEmpty(loginUser.getHeadImage())) {
            loginUserVo.setHeadImage(downloadUrl+loginUser.getHeadImage());
        }
        return loginUserVo;
    }

    @Override
    public UserVO detail() {
        // 从ThreadLocal中拿到userId
        Long userId = ThreadLocalUtil.get(Constant.USER_ID, Long.class);
        if (userId == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        // 查找用户的详细信息
        UserVO user = userCacheManager.getUserById(userId);
        if (user == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        if (StrUtil.isNotEmpty(user.getHeadImage())) {
            user.setHeadImage(downloadUrl+user.getHeadImage());
        }
        return user;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constant.USER_ID, Long.class);
        if (userId == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        // 更新当前用户的基本信息的缓存
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constant.USER_KEY, String.class));
        // 将新用户信息更新到数据库
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        Long userId = ThreadLocalUtil.get(Constant.USER_ID, Long.class);
        if (userId == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ApplicationException(AppResult.failed("用户不存在"));
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        // 更新当前用户的基本信息的缓存
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constant.USER_KEY, String.class));
        // 将新用户信息更新到数据库
        return userMapper.updateById(user);
    }

}